﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Collector.PulseFixture
{
    public class USB3000DAQ
    {
        public event DAQPortDataReceivedEventHandle DAQReceived;

        int DevIndex = -1;
        //int Running;
        byte[] Model = new byte[4];
        private bool receiveACK_;
        public bool ReceiveACK
        {
            get { return receiveACK_; }
            set { receiveACK_ = value; }
        }

        public USB3000DAQ()
        {
            ReceiveACK = true;
        }

        public bool InitUSBDevices()
        {
            int temp;

            temp = USB3000DLL.FindUSB3DAQ();
            Log.Debug(string.Format("InitUSBDevices :{0}", temp));
            if (temp <= 0)
                return false;
            else
                DevIndex = 0;
            byte[] SN = new byte[8];

            //打开指定索引号的设备
            temp = USB3000DLL.USB3OpenDevice(DevIndex);

            //显示设备序列号
            temp = USB3000DLL.USB3GetDeviceSN(DevIndex, SN);
            string serial = "";
            foreach (var item in SN)
            {
                serial += Convert.ToString(item, 16);
            }
            //显示设备型号
            temp = USB3000DLL.USB3GetDeviceModel(DevIndex, Model);
            Log.Debug("USBDeviceSN :" + serial+","+"USB-" + System.Text.Encoding.ASCII.GetString(Model));
            if ((Model[1] == 0x31) | (Model[1] == 0x32))
            {
                //对 2 个通道进行配置,Ai 相关的设置
                for (int i = 0; i < 2; i++)
                {
                    //选择是否要启用该通道
                    temp = USB3000DLL.SetUSB3AiChanSel(DevIndex, (byte)i, (byte)1);
                    //设置该通道的量程
                    temp = USB3000DLL.SetUSB3AiRange(DevIndex, (byte)i, (float)10.24);
                }

                //设置采集模式为 OneShot 模式
                temp = USB3000DLL.SetUSB3AiSampleMode(DevIndex, 0);

                //设置OneShot模式时，每通道采集 200 点
                //temp = USB3000DLL.SetUSB3AiOneShotPoints(DevIndex, 200);

                //设置 Ai 输入的接线方式为 NRSE
                temp = USB3000DLL.SetUSB3AiConnectType(DevIndex, 1);

                //设置 Ai 输入的采样周期为 10000 ns(纳秒)，即 10us，采样率为 100kHz  1ms=1000us(微秒)
                //1us = 1000ns 1ms = 100000ns
                temp = USB3000DLL.SetUSB3AiSampleRate(DevIndex, 10000);//(1 / 500) * (10 ^ 9)

                //设置 Ai 触发源为软件触发
                temp = USB3000DLL.SetUSB3AiTrigSource(DevIndex, 0);

                //设置 Ai 采样时钟为 Ai 内部采样时钟
                temp = USB3000DLL.SetUSB3AiConvSource(DevIndex, 0);

                //设置 Ai 预触发点数为 0
                temp = USB3000DLL.SetUSB3AiPreTrigPoints(DevIndex, 0);

                //清空一次 AiFifo
                temp = USB3000DLL.SetUSB3ClrAiFifo(DevIndex);

                //启动一次软件触发
                temp = USB3000DLL.SetUSB3AiSoftTrig(DevIndex);
            }
            return true;
        }
        //开启定时器接收USB数据
        public void SmacqDAQData()
        {
            Thread uploadThread = new Thread(updateDataThread);
            uploadThread.Start();
        }
        private void updateDataThread()
        {
            int temp;
            float[] ai = new float[2000];
            float[] ai_ = new float[2000];
            byte[] di = new byte[100];
            do
            {
                //每个通道读出 200 个数，存放在 ai 数组中
                temp = USB3000DLL.USB3GetAi(DevIndex, 125, ai_, 4000);
                ai = Smoothing(ai_, 3);// MovingAverage(ai_, 3);
                temp = 2;
                if (DAQReceived != null)
                {
                    DAQReceived(new DAQPortDataReciveEventArgs(ai, -100,-100));
                }
                //计算各个通道测量电压的平均值，并显示
                //for (j = 0; j < temp; j++)
                //{
                //    //Thread.Sleep(10);
                //    average = 0;
                //    for (i = 0; i < 200; i++)
                //    {
                //        average = average + ai[j * 10 + i];
                //    }
                //    average = average / 200;
                //    if (j == 0)
                //    {
                //        LogWriter(LogFile_AI0, average.ToString("F6"));
                //    }         
                //    if (j == 1)
                //    {
                //        LogWriter(LogFile_AI1, average.ToString("F6"));
                //    }

                //}
                //清除硬件触发标志
                //temp = USB3000DLL.SetUSB3ClrAiTrigger(DevIndex);
                //清空 Ai Fifo
                //temp = USB3000DLL.SetUSB3ClrAiFifo(DevIndex);
                //if (ReceiveACK)
                //{
                //    CloseDevice();
                //    break;
                //}
            } while (ReceiveACK);
        }

        //算法是否正确？
        public static float[] MovingAverage(float[] data, int span)
        {
            int b = 0;
            if (span % 2 == 1)
                b = (span - 1) / 2;
            else
            {
                span -= 1;
                b = (span - 1) / 2;
            }


            float[] smoothArray = new float[data.Length];
            if (data.Length > span)
            {
                for (int i = 0; i < data.Length; i++)
                {
                    if (i < b)
                    {
                        smoothArray[i] = 0;
                        for (int j = -i; j < i + 1; j++)
                        {
                            smoothArray[i] += data[i + j];
                        }
                        smoothArray[i] /= (2 * i + 1);
                    }
                    else if (i >= b && (data.Length - i) > b)
                    {
                        smoothArray[i] = 0;
                        for (int j = -b; j < b + 1; j++)
                        {
                            smoothArray[i] += data[i + j];
                        }
                        smoothArray[i] /= span;
                    }
                    else
                    {
                        smoothArray[i] = 0;
                        int c = data.Length - i - 1;
                        for (int j = -c; j < c + 1; j++)
                        {
                            smoothArray[i] += data[i + j];
                        }
                        smoothArray[i] /= (2 * c + 1);

                    }
                }
            }
            else
            {
                throw new ArgumentException("span is bigger than data's count");
            }

            return smoothArray;
        }

        /// <summary>
        /// 移动平均,曲线平滑
        /// </summary>
        /// <param name="rawData">原曲线数组</param>
        /// <param name="step">步长</param>
        /// <returns></returns>
        public unsafe float[] Smoothing(float[] rawData, int step = 3)
        {
            float[] smooth = new float[rawData.Length];
            fixed (float* o = smooth, r = rawData)
            {
                for (int i = step; i < rawData.Length; i++)
                {
                    float total = 0;
                    int s = step * 2 + 1;
                    for (int j = i - step; j < i + step + 1; j++)
                    {
                        if (j < rawData.Length)
                        {
                            total += r[j];
                        }
                        else
                        {
                            break;
                        }
                    }
                    o[i] = total / s;
                }

                //Head fill
                for (int i = 0; i < step; i++)
                {
                    o[i] = o[step];
                }
                //Tail fill
                int tail = rawData.Length - (rawData.Length % (step + 1)) - 1;
                for (int j = tail; j < rawData.Length; j++)
                {
                    o[j] = o[tail - 1];
                }

            }
            return smooth;
        }
        /// <summary>
        /// 中值滤波,去毛刺
        /// </summary>
        /// <param name="rawData"></param>
        /// <param name="step"></param>
        /// <returns></returns>
        //public unsafe double[] MedianFilter(double[] rawData, int step = 3)
        //{
        //    int length = step * 2 + 1;
        //    double[] smooth = new double[rawData.Length];
        //    double[] median = new double[length];
        //    fixed (double* o = smooth, r = rawData, m = median)
        //    {
        //        for (int i = step; i < rawData.Length; i++)
        //        {
        //            int s = i - step;
        //            int k = 0;
        //            for (int j = i - step; j < i + step + 1; j++)
        //            {
        //                if (j < rawData.Length)
        //                {
        //                    m[k] = r[j];
        //                }
        //                else
        //                {
        //                    break;
        //                }
        //                k++;
        //            }
        //            o[i] = SortBubbleAscendingOrder(median)[step];//排序取中间值,在我的上一篇博客有源码
        //        }
        //        //Head fill
        //        for (int i = 0; i < step; i++)
        //        {
        //            o[i] = o[step];
        //        }
        //        //Tail fill
        //        int tail = rawData.Length - (rawData.Length % (step + 1)) - 1;
        //        for (int j = tail; j < rawData.Length; j++)
        //        {
        //            o[j] = o[tail - 1];
        //        }

        //    }
        //    return smooth;
        //}

        public void CloseDevice()
        {
            try
            {
                int temp;
                Thread.Sleep(3000);
                //关闭设备
                //清除硬件触发标志
                temp = USB3000DLL.SetUSB3ClrAiTrigger(DevIndex);
                //清空 Ai Fifo
                //temp = USB3000DLL.SetUSB3ClrAiFifo(DevIndex);

                temp = USB3000DLL.USB3CloseDevice(DevIndex);
                Log.Debug(string.Format("CloseDevice :{0},{1}", temp, DevIndex));
            }
            catch (Exception ex)
            {
                Log.Debug(string.Format("CloseDeviceException :{0}", ex.Message));
            }
            
        }
        public static object lockObject1 = new object();
        private void LogWriter(string logfile, string message)
        {
            try
            {
                lock (lockObject1)
                {
                    StreamWriter sw = new StreamWriter(logfile, true);
                    sw.WriteLine(System.String.Format("{0} ", message));
                    sw.Close();
                }
            }
            catch(IOException ex)
            {
                Log.Debug(string.Format("LogWriterException :{0}", ex.Message));
            }
        }
        public static object lockObject = new object();
        private void LogWriter1(string logfile, string message)
        {
            try
            {
                lock(lockObject)
                {
                    StreamWriter sw = new StreamWriter(logfile, true);
                    sw.WriteLine(System.String.Format("{0} ", message));
                    sw.Flush();
                    sw.Close();
                }
            }
            catch (IOException ex)
            {
                Log.Debug(string.Format("LogWriterException1 :{0}", ex.Message));
            }
        }
    }

    public delegate void DAQPortDataReceivedEventHandle(DAQPortDataReciveEventArgs e);
    public class DAQPortDataReciveEventArgs : EventArgs
    {
        public float ReceiveAI0Data { get; set; }
        public float ReceiveAI1Data { get; set; }
        public float[] ReceiveDAQData { get; set; }
        public DAQPortDataReciveEventArgs(float[] DAQdata, float AI0data, float AI1data)
        {
            this.ReceiveAI0Data = AI0data;
            this.ReceiveAI1Data = AI1data;
            this.ReceiveDAQData = DAQdata;
        }
    }
    class USB3000DLL
    {
        [DllImport("USB3000.dll")]
        public static extern int FindUSB3DAQ();
        [DllImport("USB3000.dll")]
        public static extern int USB3OpenDevice(int DevIndex);
        [DllImport("USB3000.dll")]
        public static extern int USB3CloseDevice(int DevIndex);
        [DllImport("USB3000.dll")]
        public static extern int USB3GetDeviceSN(int DevIndex, byte[] SN);
        [DllImport("USB3000.dll")]
        public static extern int USB3GetDeviceModel(int DevIndex, byte[] Model);

        //--------------------------------------------------------------------------
        // Ananlog Input Configuration

        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AiSampleRate(int DevIndex, uint SamplePeriod);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AiSampleMode(int DevIndex, byte AiSampleMode);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AiConnectType(int DevIndex, byte AiConnectType);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AiRange(int DevIndex, byte Chan, float AiRange);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AiChanSel(int DevIndex, byte Chan, byte Sel);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AiTrigSource(int DevIndex, byte AiTrigSource);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AiConvSource(int DevIndex, byte AiConvSource);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AiPreTrigPoints(int DevIndex, uint AiPreTrigPoints);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AiOneShotPoints(int DevIndex, uint AiOneShotPoints);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3ClrAiFifo(int DevIndex);

        //--------------------------------------------------------------------------
        // Digital I/O Configuration

        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DiSampleRate(int DevIndex, uint SamplePeriod);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DiSampleMode(int DevIndex, byte DiSampleMode);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DiTrigSource(int DevIndex, byte DiTrigSource);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DiConvSource(int DevIndex, byte DiConvSource);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DiPreTrigPoints(int DevIndex, uint DiPreTrigPoints);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DiOneShotPoints(int DevIndex, uint DiOneShotPoints);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3ClrDiFifo(int DevIndex);

        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DoSampleRate(int DevIndex, uint SamplePeriod);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DoSampleMode(int DevIndex, byte DoSampleMode);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DoTrigSource(int DevIndex, byte DoTrigSource);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DoConvSource(int DevIndex, byte DoConvSource);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DoCycle(int DevIndex, uint DoCycle);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DoDataFifo(int DevIndex, uint[] Value, uint Len);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3ClrDoFifo(int DevIndex);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DoWaveCtrl(int DevIndex, uint Chan);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DoImmediately(int DevIndex, uint Chan, uint Value);

        //--------------------------------------------------------------------------
        // Ananlog Output Configuration

        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AoSampleRate(int DevIndex, byte Chan, uint SamplePeriod);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AoSampleMode(int DevIndex, byte Chan, byte AoSampleMode);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AoTrigSource(int DevIndex, byte Chan, byte AoTrigSource);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AoConvSource(int DevIndex, byte Chan, byte AoConvSource);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AoCycle(int DevIndex, byte Chan, byte AoCycle);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AoDataFifo(int DevIndex, byte Chan, float[] Voltage, uint Len);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3ClrAoFifo(int DevIndex, byte Chan);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AoSync(int DevIndex, byte Chans);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AoImmediately(int DevIndex, byte Chan, float Voltage);

        //--------------------------------------------------------------------------
        // Trig Control

        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AiSoftTrig(int DevIndex);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DiSoftTrig(int DevIndex);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3DoSoftTrig(int DevIndex);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3AoSoftTrig(int DevIndex, byte Chan);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3GlobalSoftTrig(int DevIndex);

        [DllImport("USB3000.dll")]
        public static extern int SetUSB3ClrTrigger(int DevIndex);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3ClrAiTrigger(int DevIndex);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3ClrDiTrigger(int DevIndex);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3ClrDoTrigger(int DevIndex);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3ClrAoTrigger(int DevIndex, byte Chan);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3ClrGlobalSoftTrig(int DevIndex);

        //--------------------------------------------------------------------------
        // Sync Configuration

        [DllImport("USB3000.dll")]
        public static extern int SetUSB3ExtTrigOutSource(int DevIndex, byte Source);
        [DllImport("USB3000.dll")]
        public static extern int SetUSB3ExtConvOutSource(int DevIndex, byte Source);

        //--------------------------------------------------------------------------
        // Get Data Acquired

        [DllImport("USB3000.dll")]
        public static extern int USB3GetAi(int DevIndex, uint Points, float[] Ai, int TimeOut);
        [DllImport("USB3000.dll")]
        public static extern int USB3GetDi(int DevIndex, uint Points, byte[] Di, int TimeOut);
    }
}
